package com.ushine.web.component.authority.handler;

import com.ushine.web.base.model.redis.UserRedisBO;
import com.ushine.web.base.model.system.DataPermissionDO;
import com.ushine.web.base.facade.IUserRedisFacade;
import com.ushine.web.component.authority.utils.DataColumnUtils;
import com.ushine.web.component.exception.ServiceException;
import com.ushine.web.component.exception.ServiceExceptionEnum;
import com.ushine.web.security.utils.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.*;
import java.math.BigDecimal;
import java.util.*;

/**
 * @Author: yls
 * @Date: 2022/02/24 09:37
 * @Description: 数据权限：控制数据添加的字段
 * @Version 1.0
 */
@Slf4j
@Component
public class InsertDataPermissionHandler {

    @Resource
    private IUserRedisFacade userRedisFacade;

    /**
     * 功能描述:
     * 〈 添加时的字段过滤 〉
     *
     * @param obj                过滤对象
     * @param dataPermissionFlag 数据权限表达式
     * @return : T
     * @throws ServiceException 服务端自定义异常
     * @author : yls
     * @date : 2022/02/24 11:16
     */
    public <T> T getInsertDataObject(T obj, String dataPermissionFlag) throws ServiceException {
        /*
         * 获取当前用户拥有的列级权限
         */
        String username = SecurityUtils.getUsername();
        UserRedisBO userInfo = userRedisFacade.getUserInfo(username);
        DataPermissionDO dataPermission = userInfo.getDataPermissionMap().get(dataPermissionFlag);
        // 没有设置数据权限，默认任务拥有【所有列】的【添加】数据权限
        if (dataPermission == null) {
            return obj;
        }
        // 获取列权限
        // 过滤列（不包含这些列）
        String dataColumnExclude = dataPermission.getDataColumnExclude();
        Map<String, Integer> excludeColumnMap = DataColumnUtils.getColumnSetByString(dataColumnExclude);
        // 包含列（只有这些列权限）
        String dataColumnInclude = dataPermission.getDataColumnInclude();
        Map<String, Integer> includeColumnMap = DataColumnUtils.getColumnSetByString(dataColumnInclude);
        if (excludeColumnMap.isEmpty() && includeColumnMap.isEmpty()) {
            return obj;
        }
        /*
         * 遍历实体的字段
         */
        try {
            filterColumn(obj, includeColumnMap, excludeColumnMap);
        } catch (Exception e) {
            log.error("添加时的字段过滤出错", e);
            throw new ServiceException(ServiceExceptionEnum.DATA_PERMISSION_INSERT_ERROR, e);
        }
        return obj;
    }

    /**
     * 功能描述:
     * 〈 根据权限过滤字段 〉
     *
     * @param obj              带过滤对象
     * @param excludeColumnMap 过滤的字段
     * @param includeColumnMap 包含的字段
     * @return : void
     * @author : yls
     * @date : 2022/02/24 11:14
     */
    public <T> void filterColumn(T obj, Map<String, Integer> includeColumnMap, Map<String, Integer> excludeColumnMap) throws IllegalAccessException, InvocationTargetException, InstantiationException, ClassNotFoundException {
        Class<?> aClass = obj.getClass();
        List<Field> fieldList = new ArrayList<>();
        //当父类为null的时候说明到达了最上层的父类(Object类).
        while (aClass != null) {
            fieldList.addAll(Arrays.asList(aClass.getDeclaredFields()));
            //得到父类,然后赋给自己
            aClass = aClass.getSuperclass();
        }
        for (Field field :
                fieldList) {
            String name = field.getName();
            // 系统后台自带字段不参加过滤
            boolean flag = DataColumnUtils.EXCLUDE_COLUMN_MAP.containsKey(name) || (!includeColumnMap.isEmpty() && includeColumnMap.containsKey(name));
            if (Boolean.TRUE.equals(flag)) {
                continue;
            }
            // 如果只设置了包含字段，
            if (!includeColumnMap.isEmpty() && !includeColumnMap.containsKey(name)) {
                // 先过滤包含字段
                initPropertyDefaultValue(obj, field);
            }

            // 如果只设置了不包含字段
            // 开始处理权限访问字段
            if (!excludeColumnMap.isEmpty() && excludeColumnMap.containsKey(name)) {
                log.info("name:{}", name);
                initPropertyDefaultValue(obj, field);
            }
        }
    }

    /**
     * 功能描述:
     * 〈 对于无权限操作的字段回填默认值 〉*
     *
     * @param obj   过滤对象
     * @param field 字段
     * @return : void
     * @author : yls
     * @date : 2022/02/24 11:11
     */
    private <T> void initPropertyDefaultValue(T obj, Field field) throws IllegalAccessException, InvocationTargetException, InstantiationException, ClassNotFoundException {
        // 设置取消属性访问检查，可以访问私有属性
        field.setAccessible(true);
        List<Method> methods = new ArrayList<>();
        Class<?> aClass = obj.getClass();
        //当父类为null的时候说明到达了最上层的父类(Object类).
        while (aClass != null) {
            methods.addAll(Arrays.asList(aClass.getDeclaredMethods()));
            //得到父类,然后赋给自己
            aClass = aClass.getSuperclass();
        }
        Map<String, Method> methodNameMethodMap = new HashMap<>(methods.size());
        for (Method method : methods) {
            methodNameMethodMap.put(method.getName().toLowerCase(), method);
        }
        Class<?> clazz = field.getType();
        String propertyName = field.getName();
        Method method = methodNameMethodMap.get("set" + propertyName.toLowerCase());
        if (method == null) {
            return;
        }
        if (DataColumnUtils.isWrapClass(clazz) || DataColumnUtils.isCommonDataType(clazz)) {
            if (clazz == Boolean.class) {
                method.invoke(obj, Boolean.TRUE);
            } else if (clazz == Byte.class) {
                method.invoke(obj, new Byte("0"));
            } else if (clazz == Short.class) {
                method.invoke(obj, new Short("0"));
            } else if (clazz == Integer.class) {
                method.invoke(obj, 0);
            } else if (clazz == Long.class) {
                method.invoke(obj, 0L);
            } else if (clazz == Float.class) {
                method.invoke(obj, 0F);
            } else if (clazz == Double.class) {
                method.invoke(obj, 0D);
            } else if (clazz == Character.class){
                method.invoke(obj, '0');
            }
        } else if (BigDecimal.class.isAssignableFrom(clazz)) {
            method.invoke(obj, new BigDecimal("0"));
        } else if (Date.class.isAssignableFrom(clazz)) {
            method.invoke(obj, new Date());
        } else if (String.class.isAssignableFrom(clazz)) {
            method.invoke(obj, "");
        } else if (Collection.class.isAssignableFrom(clazz) || Map.class.isAssignableFrom(clazz)) {
            Type[] type = method.getGenericParameterTypes();
            ParameterizedType pType = (ParameterizedType) type[0];
            Collection collection = null;
            String list = "List";
            String set = "Set";
            if (pType.getRawType().toString().endsWith(list)) {
                collection = new ArrayList<>(0);
            } else if (pType.getRawType().toString().endsWith(set)) {
                collection = new HashSet<>(0);
            }
            Object objValue = Class.forName(pType.getActualTypeArguments()[0].getTypeName()).newInstance();
            if (objValue == null) {
                return;
            }
            method.invoke(obj, collection);
        }
    }


}
